#include "Classe.h"
using namespace std;

string Classe::Caracteristiques[6] =
  {
    "Force       ",
    "Dextérité   ",
    "Constitution",
    "Intelligence",
    "Sagesse     ",
    "Charisme    "
  };

DebugLevelType Classe::elementaire  = INFO;
DebugLevelType Classe::fonction     = INFO;

Classe::Classe()
{}

/**
 * Constructeur standard.
 * @param xp Nombre de pt d'expérience du personnage
 * @param trait tableau des traits du personnage
 * @param tc type de classe (càd Combattant, Mage, Prêtre ou Roublard)
 * du personnage
 */
Classe::Classe(unsigned int xp, unsigned int trait[7], TypeClasse tc)
  : XP(xp)
{
  for(unsigned int i(0); i < 7; i++)
    Traits[i] = trait[i];

  ptrComp    = 0;
  ptrRes     = 0;
  ptrValBase = 0;
  ptrNbrComp = 0;
  ptrNbrRes  = 0;

  Niveau = 0;
  TC.push_back(Toutes);
  TC.push_back(tc);
}

/**
 * Constructeur de copie, réalise la copie profonde.
 * @param classe classe à copier
 */
Classe::Classe(const Classe& classe)
{
  ptrComp    = classe.ptrComp;
  ptrRes     = classe.ptrRes;
  ptrValBase = classe.ptrValBase;
  ptrNbrComp = classe.ptrNbrComp;
  ptrNbrRes  = classe.ptrNbrRes;

  XP = classe.XP;
  for(unsigned int i(0); i<7; i++)
    Traits[i] = classe.Traits[i];
  Niveau = classe.Niveau;
  unsigned int taille(classe.Comp_classe.size());
  for(unsigned int i(0); i<taille; i++)
    Comp_classe.push_back(classe.Comp_classe[i]);
  TC.assign(classe.get_TC() -> begin(), classe.get_TC() -> end());
}

/**
 * Destructeur, ne fait rien.
 */
Classe::~Classe()
{}

/**
 * Sert à afficher le niveau du personnage (si le personnage est multi-classé,
 * affiche le niveau de la Classe.
 */
void Classe::affiche() const
{
  blank() << "Niveau : " << Niveau << endreq;
}

/**
 * Affiche un récapitulatif de la classe comprenant les traits, les
 * restrictions et les compétences.
 * \e FUNC permet d'afficher le nom au début et à la fin de la méthode
 * \e DEBUG permet d'afficher chaque élément avec son type (compétence, restriction)
 */
void Classe::afficher_stats(ostream& os)
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(elementaire);
  func() << "Classe::afficher_stats -> début" << endreq;

  os << "Classe : " << Type_classe << endreq;

  for (unsigned int i(0); i < 6; i++)
    os << Caracteristiques[i] << ' ' << Traits[i] << endreq;

  affiche_competences(os);
  // for (i = 0; i < taille; i++)
//     {
//       debug() << i << "-ème élément : " << Comp_classe[i];
//       if (Comp_classe[i] >= *ptrNbrComp)
// 	{
// 	  debug() << " => restriction" << endreq;
// 	  os << affiche_accent( ptrRes -> at( Comp_classe[i] - *ptrNbrComp ).Comp)
// 	     << endreq;
// 	}
//       else
// 	{
// 	  debug() << " => compétence" << endreq;
// 	  os << affiche_accent( ptrComp -> at( Comp_classe[i] ).Comp ) << endreq;
// 	}
//     }

  func() << "Classe::afficher_stats -> début" << endreq;
  dout.setLevel(etat_precedent);
}

/**
 * Sert à déterminer les compétences de classe du personnage. Est appelé
 * lors de la lecture d'un fichier xml.
 * @param capa est le vecteur qui contient les compétences
 */
void Classe::set_Competences(const vector<unsigned int> capa)
{
  Comp_classe.assign( capa . begin(), capa . end() );
}

/**
 * Affiche les compétences de classe.
 * Est destimé à faire un récapitulatif des compétences de classe,
 * lors de l'utilisation du soft en mode console.
 * \e FUNC permet d'afficher le nom au début et à la fin de la méthode
 * \e DEBUG permet d'afficher le nombre de compétences
 * @param os flot, blank() est utilisé par défaut
 */
void Classe::affiche_competences(ostream& os) const
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(fonction);
  func() << "Classe::affiche_competences -> début" << endreq;
  
  os << "Récapitulation des compétences de classe : " << endreq;
  unsigned int taille(get_combien());
  string comp_str;

  debug() << "Nbr de comp : " << taille << endreq;

  for (unsigned int i(0); i < taille; i++)
    {
      comp_str = get_competence(i);
      os << " " << comp_str << endreq;
      if (comp_str.find("%") != string::npos)
	i++;
    }
  
  func() << "Classe::affiche_competences -> fin" << endreq;
  dout.setLevel(etat_precedent);
}

/**
 * Permet de retrouver le nom de la Classe.
 * @return le nom de la classe ("Guerrier", "Voleur", ...)
 */
string Classe::get_classe() const
{
  return Type_classe;
}

/**
 * Donne le nombre courant de pts d'expérience.
 * @return nombre de pts d'expérience
 */
unsigned int Classe::get_XP() const
{
  return XP;
}

/**
 * Récupère le trait désiré (y compris le score de force expectionnel.
 * @param i indice du trait voulu
 * @return la valeur du trait voulu (unsigned int), ou 999 en cas d'erreur.
 */
unsigned int Classe::get_trait(const unsigned int& i) const
{
  if (i < 7)
    return Traits[i];
  else
    return 999;
}

/**
 * Donne le nombre de compétences.
 * @return taille du conteneur Comp_classe, si une (ou plusieurs) compétence
 * est associée à un pourcentage, ce nombre n'est pas le nombre de compétences.
 */
unsigned int Classe::get_combien() const
{
  return Comp_classe.size();
}

/**
 * Retourne un compétence sous forme d'une string.
 * @param i numéro de la compétence
 * @return nom de la compétence i, ainsi que l'éventuel pourcentage associé.
 */
unsigned int Classe::get_comp(const unsigned int& i) const
{
  if (i < Comp_classe.size())
    return Comp_classe[i];
  else
    return 255;
}

/**
 * Permet de récupérer le nom de la compétence donnée, avec l'éventuel
 * pourcentage associé.
 * \e FUNC permet d'afficher le nom au début et à la fin de la méthode
 * \e DEBUG permet d'afficher le numéro de la compétence
 * @param i numéro de la compétence désirée
 * @return le nom de la compétence, suivi par le pourcentage si existant
 */
string Classe::get_competence(const unsigned int& i) const
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(fonction);
  func() << "Classe::get_competence -> début" << endreq;

  debug() << "Compétence numéro " << i << endreq;
  
  ListComp tmp;

  if ( Comp_classe[i] >= *ptrNbrComp )
    tmp = ptrRes -> at( Comp_classe[i] - *ptrNbrComp );
  else
    tmp =  ptrComp -> at( Comp_classe[i] ) ;

  if (tmp.Pourcentage)
    {
      dout.setLevel(etat_precedent);
      func() << "Classe::get_competence -> fin" << endreq;
      return " " + tmp.Comp + " " + write_int(get_comp(i+1)) + "\\%";
    }
  else
    {
      dout.setLevel(etat_precedent);
      func() << "Classe::get_competence -> fin" << endreq;
      return tmp.Comp;
    }
}

unsigned int Classe::get_valeur_base(const unsigned int& i) const
{
  if ( ptrValBase == 0)
    return 0;
  else if ( ptrComp -> at(i) . Pourcentage )
    return ptrValBase -> at(i);
  else
    return 0;
}

/**
 * Retourne le niveau du personnage
 * @return niveau du personnage.
 */
unsigned int Classe::get_Niveau() const
{
  return Niveau;
}

/**
 * Retourne le niveau du personnage
 * @return niveau du personnage.
 */
unsigned int Classe::get_Niveau_magie() const
{
  return Niveau_mag;
}

/**
 * Sert à virer les accents dans le nom des écoles de magies et sphères
 * (écrit en syntaxe latex pour plus de logique)
 * Supprime en même temps le 2ème nom de l'école (càd Illusion - Fantasme =>
 * Illusion)
 * \e FUNC permet d'afficher le nom au début et à la fin de la méthode
 * \e LFUNC permet d'afficher l'évolution de la chaîne de caractère lors du
 * traitement des accents
 * \e DEBUG affiche l'évolution lors de la troncature
 * @param brut string comprenant des accents en syntaxe Latex et / ou des
 * espaces.
 * @return string sans accents ni espaces
*/
string Classe::supprime_accents(const string& brut) const
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(elementaire);
  func() << "Classe::supprime_accents -> début" << endreq;


  string retour(brut);
  size_t loc(retour.find("\\'", 0));
  while (loc != string::npos)
    {
      lfunc() << retour << ' ' << loc << endreq;
      retour.replace(loc, 2, "");
      loc = retour.find("\\'", 0);
      lfunc() << retour << endreq;
    }

  loc = retour.find(" -", 0);
  if (loc != string::npos)
    {
      // ici il faut tronquer le nom
      debug() << retour << " -> ";
      retour.replace(loc, retour.size() - loc, "");
      debug() << retour << endreq;
    }
    

  dout.setLevel(etat_precedent);
  func() << "Classe::supprime_accents -> fin" << endreq;
  return retour;
}

/**
 * Retourne le TypeClasse du personnage.
 * @return TC, le type de classe du personnage.
 */
const vector<TypeClasse>* Classe::get_TC() const
{
  return &TC;
}

/**
 * Faire progresser le personnage, en augmentant son nombre de pts
 * d'expérience
 * @param xp le nombre de pts d'expérience gagnés.
 */
void Classe::augmenter_xp(const unsigned int& xp)
{
  XP += xp;
  calcul_niveau();
}

/**
 * Gestion des niveau de sortie des fonctions.
 * @param level le niveau (INFO, DEBUG, ...).
 * @param n permet de sélectionner quel variable est affectée.
 */
void Classe::setLevel(const DebugLevelType& level, const unsigned int& n)
{
  switch(n)
    {
    case 1:
      elementaire = level;
      break;
    case 2:
      fonction = level;
      break;
    default:
      warning() << "Variable inconnue, dans Classe::setLevel " << n << endreq;
    }
}

/**
 * Permet de remplir son Livre de sorts à partir des écoles accessibles.
 * L'ajout est sécurisé (impossible de prendre deux fois le sort).
 */
void Classe::ajout_sorts()
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(fonction);
  func() << "On est dans Classe, rien à faire ici" << endreq; 
  dout.setLevel(etat_precedent);
}

/**
 * Ajoute des sorts à la classe. On vérifie d'abord que la classe en a, puis
 * on ajoute les nouveaux et on trie le tout.
 * @param vec le conteneur des nouveaux sorts
 * \e FUNC permet d'afficher le nom au début et à la fin de la méthode
 * \e DEBUG affiche le nombre de sort qui seront ajoutés
 */
void Classe::ajout_sorts(const vector<Sort>& vec)
{
  
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(fonction);
  func() << "Classe ajout_sorts -> début" << endreq;

  if (Niveau_mag > 0)
    {
      debug() << vec.size() << " sort à ajouter" << endreq;
      Cl_sorts.insert(Cl_sorts.end(), vec.begin(), vec.end());

      vector<Sort>::iterator Begin, End, it;
      Begin = Cl_sorts.begin();
      End   = Cl_sorts.end();

      sort(Begin, End);

      unique(Begin, End);
    }
  else
    debug() << "Rien à faire, niveau magique nul" << endreq;
  
  func() << "Classe ajout_sorts -> fin" << endreq;
  dout.setLevel(etat_precedent);
}

/**
 * Est utilisé lors de la lecture d'un personnage à partir d'un fichier XML.
 * Elle permet de recréer le Livre de sorts du personnage.
 * @param sorts sorts connus par le personnage.
 * \e FUNC permet d'afficher le nom au début et à la fin de la méthode
 * \e DEBUG affiche le nombre de sort qui seront copiés
 */
void Classe::set_Sorts(const vector<Sort>& sorts)
{
  DebugLevelType etat_precedent(dout.getLevel());
  dout.setLevel(elementaire);
  unsigned int taille(sorts.size());
  func() << "Classe set_Sorts -> début" << endreq;

  debug() << "Nbr de sort à copier : " << taille << endreq;

  Cl_sorts.clear();

  for (unsigned int i(0); i < taille; i++)
    Cl_sorts.push_back(sorts[i]);

  func() << "Classe set_Sorts -> fin" << endreq;
  dout.setLevel(etat_precedent);
}

/**
 * Est utilisé pour fixer le nombre de xp juste après la création (si le
 * personnage n'est pas niveau 1 lors de la création)
 */
void Classe::set_xp(const unsigned int& xp)
{
  XP = xp;

  calcul_niveau();

  calcul_niv_mag();
}

/**
 * Donne le nombre de sorts que le personnage connait.
 * @return taille du containeur Cl_Sorts
 */
unsigned int Classe::get_combien_sorts() const
{
  if (Niveau_mag > 0)
    return this -> Cl_sorts.size();
  else
    return 0;
}

/**
 * Permet de récupérer le conteneur des sorts de la classe (pour magiciens
 * et bardes uniquement).
 * @return un pointeur sur le conteneur (const vector<Sort>*).
 */
const vector<Sort>* Classe::get_sort_adresse() const
{
  if (get_combien_sorts() > 0)
    return const_cast<const vector<Sort>*>( &(this -> Cl_sorts) );
  else
    return 0;
}

void Classe::get_competences(unsigned int& nbrComp,
			     vector<ListComp>& comp,
			     unsigned int& nbrRes,
			     vector<ListComp>& restric) const
{
  nbrComp = *ptrNbrComp;
  nbrRes  = *ptrNbrRes;

  comp.assign( ptrComp -> begin(), ptrComp -> end());

  if (ptrRes != 0)
    restric.assign( ptrRes -> begin(), ptrRes -> end());
  else
    restric.clear();
}

/**
 * Permet de récupérer le nombre d'ensembles de capacités raciales, ainsi
 * que le nombre maximal de ces capacités raciales. Sert pour les
 * spécialistes, avec une possible extension au Clers, Druides.
 * @param nbr nombre d'ensembles différents
 * @param max nombre maximal de capacités dans un ensemble
 */
void Classe::get_ensembles_capacites(unsigned int& nbr, unsigned int& max) const
{
  nbr = 0;
  max = 0;
}

/**
 * Permet de récupérer l'ensemble de capacités raciales désiré.
 * @param nbr numéro de l'ensemble voulu
 * @return pointeur sur la première capacité de l'ensemble, ici toujours
 * nul, car uniquement certaines classes possèdent de tels ensembles.
 */
const unsigned int* Classe::get_ensembles_capacites(const unsigned int& nbr)
  const
{
  return 0;
}

/**
 * Permet de récupérer de nom de l'ensemble de capacités raciales désiré.
 * @param nbr numéro de l'ensemble voulu
 * @return pointeur sur la première capacité de l'ensemble, ici toujours
 * nul, car uniquement certaines classes possèdent de tels ensembles.
 */
const string* Classe::get_nom_ensemble(const unsigned int& nbr) const
{
  return 0;
}

/**
 * Retourne le sort désiré. En cas d'erreur, le sort retourné n'existe pas
 * (niveau 0 inexistant).
 * @param i numéro du sort
 * @return le sort numéro i
 */
Sort Classe::get_sort(const unsigned int& i) const
{
  if (i < get_combien_sorts())
    return Cl_sorts[i];
  else
    return Sort(0, 0, 0);
}

/**
 * Permet de savoir combien de PP sont alloués lors de la création d'une classe
 * donnée.
 * @return le nombre de PP alloués.
 */
unsigned int Classe::get_PP() const
{
  if (Type_classe == "Guerrier")
    return 15;
  else if (Type_classe == "Paladin" || Type_classe == "Rôdeur")
    return 60;
  else if (Type_classe == "Clerc")
    return 120;
  else if (Type_classe == "Druide")
    return 100;
  else if (Type_classe == "Mage")
    return 40;
  else if (Type_classe == "Spécialiste")
    return 30;
  else if (Type_classe == "Barde")
    return 70;
  else if (Type_classe == "Voleur")
    return 80;
  else
    return 0;
}

/**
 * Permet de récupérer les compétences de classe. Le conteneur est écrasé
 * lors de l'opération.
 * @param vec le conteneur des compétences (vector<unsigned int>).
 */
const vector<unsigned int>* Classe::get_toutes_competences(unsigned int& nbr)
  const
{
  nbr = get_combien();

  return &Comp_classe;
}

void Classe::prepare_affichage()
{
  calcul_niveau();
  calcul_niv_mag();
}

ListComp Classe::get_competence_numero(const unsigned int& i) const
{
  if ( ptrComp == 0 )
    return ListComp();
  else
    return ( ptrComp -> at( i ) );
}

bool Classe::possede_pourcent() const
{
  // Temporaire, après ptrComp sera bien défini tout le temps
  if (ptrComp == 0)
    return false;

  unsigned int taille(get_combien());

  for (unsigned int i(0); i < taille; i++)
    {
      if ( ptrComp -> at( get_comp( i ) ) . Pourcentage )
	return true;
    }

  return false;
}

unsigned int Classe::get_pourcent(const bool& lvlUp = true) const
{
  return 0;
}
